#include "Sim900.h"
#include "Assert.h"

module Sim900DriverLayerP
{
	
	provides interface SplitControl as Sim900Control;
	provides interface Sim900DriverLayer;
	provides interface Init;
	
	uses interface UartStream;
	uses interface StdControl as UartControl;
	uses interface Leds;
	uses interface Timer<TMilli> as Watchdog;
	uses interface Timer<TMilli>;
	uses interface GeneralIO as PowerPin;
	uses interface GeneralIO as PowerLED;
#ifdef SIM900_DRIVER_DEBUG
	uses interface DiagMsg;
#endif
}
implementation
{
	norace uint8_t SIMSTATE;
	norace char cmdBuf[COMMAND_BUFFER_SIZE];
	norace char dataBuf[255];
	norace char sizeBuf[4];
	norace char *pDataSend = NULL;
	norace uint8_t dataSendLen = 0;
	norace char *numStrs = "0123456789";
	norace char rcvBuf[RECEIVE_BUFFER_SIZE];
	norace uint8_t rcvCount = 0;
	norace error_t atSuccess = FAIL;
	
	bool inFrame = FALSE;
	
#ifdef SIM900_DRIVER_DEBUG
	void DiagStr(unsigned char *str)
	{
		if(call DiagMsg.record())
		{
			call DiagMsg.str(str);
			call DiagMsg.send();
		}
	}
	
	void DiagStrNum(unsigned char *str, uint8_t num)
	{
		if(call DiagMsg.record())
		{
			call DiagMsg.str(str);
			call DiagMsg.uint8(num);
			call DiagMsg.send();
		}
	}
#endif
	
	inline void uint8ToStr(uint8_t num, char *numStr);	

	task void SignalDone()
	{
		rcvCount = 0;
		
		call Watchdog.stop();
		call Timer.stop();
		
		if(SIMSTATE == SIMSTATE_ONING)
		{
			call Leds.led3Off();
			
			SIMSTATE = SIMSTATE_OFF;
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStrNum("SGN-START1", FAIL);
			#endif
			
			signal Sim900Control.startDone(FAIL);
		}
		if(SIMSTATE == SIMSTATE_REGISTERING)
		{
			call Leds.led3Off();
			if(atSuccess == SUCCESS) SIMSTATE = SIMSTATE_IDLE;
			else SIMSTATE = SIMSTATE_OFF;
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStrNum("SGN-START2", atSuccess);
			#endif
			
			signal Sim900Control.startDone(atSuccess);
		}
		else if(SIMSTATE == SIMSTATE_OFFING)
		{
			call Leds.led3Off();
			SIMSTATE = SIMSTATE_OFF;
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStrNum("SGN-STOP", atSuccess);
			#endif
			
			signal Sim900Control.stopDone(atSuccess);
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_CONN)
		{
			call Leds.led3Off();
			if(atSuccess == SUCCESS) SIMSTATE = SIMSTATE_SOCKET_IDLE;
			else SIMSTATE = SIMSTATE_IDLE;
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStrNum("SGN-SOCK", atSuccess);
			#endif
			
			signal Sim900DriverLayer.openSocketDone(atSuccess);
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_SEND_CONS || SIMSTATE == SIMSTATE_SOCKET_SEND)
		{
			call Leds.led3Off();
			SIMSTATE = SIMSTATE_SOCKET_IDLE;
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStrNum("SGN-SEND", atSuccess);
			#endif
			
			signal Sim900DriverLayer.writeSocketDone(atSuccess);
		}
		if(SIMSTATE == SIMSTATE_SOCKET_CLOSE)
		{
			call Leds.led3Off();
			SIMSTATE = SIMSTATE_IDLE;
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStrNum("SGN-CLOSE", atSuccess);
			#endif
			
			signal Sim900DriverLayer.closeSocketDone(atSuccess);
		}
		if(SIMSTATE == SIMSTATE_SOCKET_DISPOSED)
		{
			SIMSTATE = SIMSTATE_IDLE;
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStr("SGN-DISPOSED");
			#endif
			
			signal Sim900DriverLayer.socketDisposed();
		}
	}
	
	error_t sendCommand(char *cmd, uint32_t timeout)
	{
		if(cmd != NULL)
		{
			strcpy(cmdBuf, cmd);
			strcat(cmdBuf, "\r");
		}
		
		if(timeout != 0) call Watchdog.startOneShot(timeout);
		inFrame = FALSE;
		rcvCount = 0;
		return call UartStream.send(cmdBuf, strlen(cmdBuf));
	}
	
	error_t sendData(char *data, uint8_t len, uint32_t timeout)
	{
		if(timeout != 0) call Watchdog.startOneShot(timeout);
		
		inFrame = FALSE;
		rcvCount = 0;
		return call UartStream.send(data, len);
	}
	
	task void commandTask()
	{
		if(SIMSTATE == SIMSTATE_REGISTERING)
		{
			
			if(sendCommand("AT+CREG?", 5000) != SUCCESS)
			{
				#ifdef SIM900_DRIVER_DEBUG
				DiagStr("ERR-CREG-OVF");
				#endif
			}
			else
			{
				#ifdef SIM900_DRIVER_DEBUG
				DiagStr("T-CMD-CREG");
				#endif
				
				call Leds.led3On();
			}
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_CONN)
		{
			strcpy(cmdBuf, "AT+CIPSHUT;");
			strcat(cmdBuf, "+CIPMUX=0;");
			strcat(cmdBuf, "+CSTT=\"");
			strcat(cmdBuf, SIM900_APN);
			strcat(cmdBuf, "\",\"");
			strcat(cmdBuf, SIM900_USER);
			strcat(cmdBuf, "\",\"");
			strcat(cmdBuf, SIM900_PASSWD);
			strcat(cmdBuf, "\";");
			strcat(cmdBuf, "+CIICR;");
			strcat(cmdBuf, "+CIFSR;");
			strcat(cmdBuf, "+CIPSTART=\"");
			strcat(cmdBuf, SIM900_PROTOCOL);
			strcat(cmdBuf, "\",\"");
			strcat(cmdBuf, SIM900_SERVER);
			strcat(cmdBuf, "\",\"");
			strcat(cmdBuf, SIM900_PORT);
			strcat(cmdBuf, "\"");
			
			strcat(cmdBuf, "\r");
			
			if(sendCommand(NULL, 30000) != SUCCESS)
			{
				
				#ifdef SIM900_DRIVER_DEBUG
				DiagStr("ERR-CONN-OVF");
				#endif
				
				atSuccess = FAIL;
				post SignalDone();
			}
			else
			{
				#ifdef SIM900_DRIVER_DEBUG
				DiagStr("T-CMD-CONN");
				#endif
				
				call Leds.led3On();
			}
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_SEND_CONS)
		{
			uint8ToStr(dataSendLen, sizeBuf);
			
			strcpy(cmdBuf, "AT+CIPSEND=");
			strcat(cmdBuf, sizeBuf);
			
			strcat(cmdBuf, "\r");
			
			if(sendCommand(NULL, 5000) != SUCCESS)
			{
				
				#ifdef SIM900_DRIVER_DEBUG
				DiagStr("ERR-SEND-OVF");
				#endif
				
				atSuccess = FAIL;
				post SignalDone();
			}
			else
			{
				#ifdef SIM900_DRIVER_DEBUG
				if(call DiagMsg.record())
				{
					call DiagMsg.str("T-CMD-SEND");
					call DiagMsg.str(sizeBuf);
					call DiagMsg.send();
				}
				#endif
				
				call Leds.led3On();
			}
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_SEND)
		{
			if(sendData(pDataSend, dataSendLen, 5000) != SUCCESS)
			{
				atSuccess = FAIL;
				post SignalDone();
			}
			else call Leds.led3On();
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_CLOSE)
		{
			if(sendCommand("AT+CIPSHUT", 10000) != SUCCESS)
			{
				atSuccess = FAIL;
				post SignalDone();
			}
			else
			{
				
				#ifdef SIM900_DRIVER_DEBUG
				DiagStr("T-CMD-SHUT");
				#endif
				
				call Leds.led3On();
			}
		}
	}
	
	event void Timer.fired()
	{
		if(SIMSTATE == SIMSTATE_ONING)
		{
			call PowerPin.clr();
			
			if(call PowerLED.get())
			{
				call Leds.led3Off();
				SIMSTATE = SIMSTATE_REGISTERING;
				call Timer.startPeriodic(DELAY_REGISTERING);
			}
			else
			{
				post SignalDone();
			}
		}
		else if(SIMSTATE == SIMSTATE_OFFING)
		{
			call UartControl.stop();
			
			call PowerPin.clr();
			
			if(! call PowerLED.get()) atSuccess = SUCCESS;
			else atSuccess = FAIL;
			
			post SignalDone();
		}
		else if(SIMSTATE == SIMSTATE_REGISTERING)
		{
			post commandTask();
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_CONN_DELAY)
		{
			SIMSTATE = SIMSTATE_SOCKET_CONN;
			post commandTask();
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_SEND_CONS_DELAY)
		{
			SIMSTATE = SIMSTATE_SOCKET_SEND_CONS;
			post commandTask();
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_SEND_DELAY)
		{
			SIMSTATE = SIMSTATE_SOCKET_SEND;
			post commandTask();
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_CLOSE_DELAY)
		{
			SIMSTATE = SIMSTATE_SOCKET_CLOSE;
			post commandTask();
		}
	}
	
	async event void UartStream.receivedByte(uint8_t byte)
	{
		rcvBuf[rcvCount++] = byte;
		rcvBuf[rcvCount] = '\0';
		
		if(rcvCount == RECEIVE_BUFFER_SIZE)
		{
			while(1) call Leds.set(0xF);
			atSuccess = FAIL;
			post SignalDone();
			
			return;
		}
		
		if(!inFrame && byte == '\n' && SIMSTATE != SIMSTATE_SOCKET_SEND_CONS) inFrame = TRUE;
		
		
		if(inFrame && byte == '\n')
		{
			inFrame = FALSE;
			if(SIMSTATE == SIMSTATE_REGISTERING)
			{
				if(strstr(rcvBuf, "ERROR") != NULL)
				{
					call Timer.stop();
					atSuccess = FAIL;
					post SignalDone();
				}
				else if(strstr(rcvBuf, "+CREG: 0,1\r\n\r\nOK") != NULL)
				{
					call Timer.stop();
					atSuccess = SUCCESS;
					post SignalDone();
				}
				else if(strstr(rcvBuf, "+CREG: 0,0\r\n\r\nOK") != NULL)
				{
					call Watchdog.stop();
				}
			}
			else if(SIMSTATE == SIMSTATE_SOCKET_CONN)
			{	
				if(strstr(rcvBuf, "ERROR") != NULL || strstr(rcvBuf, "CONNECT FAIL") != NULL)
				{
					atSuccess = FAIL;
					post SignalDone();
				}
				else if(strstr(rcvBuf, "CONNECT OK") != NULL || strstr(rcvBuf, "ALREADY CONNECT") != NULL)
				{
					atSuccess = SUCCESS;
					post SignalDone();
				}
				
			}
			else if(SIMSTATE == SIMSTATE_SOCKET_IDLE)
			{	
				if(strstr(rcvBuf, "CLOSED") != NULL)
				{
					SIMSTATE = SIMSTATE_SOCKET_DISPOSED;
					atSuccess = FAIL;
					post SignalDone();
				}
				else if(rcvBuf[rcvCount - 1] == '\n' && rcvBuf[rcvCount - 2] == '\r')
				{
					#ifdef SIM900_DRIVER_DEBUG
					DiagStrNum("E-RCV",rcvCount);
					#endif
					
					signal Sim900DriverLayer.socketDataReceived(rcvBuf);
					
					rcvCount = 0;
				}
				else
				{
					#ifdef SIM900_DRIVER_DEBUG
					DiagStr("ERR-RCV-INV");
					#endif
				}
			}
			
			else if(SIMSTATE == SIMSTATE_SOCKET_SEND)
			{	
				if(strstr(rcvBuf, "ERROR") != NULL)
				{
					atSuccess = FAIL;
					post SignalDone();
				}
				else if(strstr(rcvBuf, "SEND OK") != NULL)
				{
					atSuccess = SUCCESS;
					post SignalDone();
				}
			}
			else if(SIMSTATE == SIMSTATE_SOCKET_CLOSE)
			{	
				if(strstr(rcvBuf, "SHUT OK") != NULL)
				{
					atSuccess = SUCCESS;
					post SignalDone();
				}
			}
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_SEND_CONS)
		{	
			if(rcvCount >= 8 && strstr(rcvBuf, "ERROR") != NULL)
			{
				atSuccess = FAIL;
				post SignalDone();
			}
			else if(rcvCount >= 4 && strstr(rcvBuf, "\r\n> ") != NULL)
			{
				call Watchdog.stop();
				rcvCount = 0;
				
				SIMSTATE = SIMSTATE_SOCKET_SEND_DELAY;
				call Timer.startOneShot(DELAY_WR_SOCKET);
			}
				
		}
	}
	
	command error_t Init.init()
	{
		SIMSTATE = SIMSTATE_OFF;
		call PowerPin.makeOutput();
		call PowerPin.clr();
		
		return SUCCESS;
	}
	
	command error_t Sim900Control.start()
	{
		ASSERT(SIMSTATE == SIMSTATE_OFF);
		
		if(SIMSTATE != SIMSTATE_OFF) return EALREADY;
		else
		{
			SIMSTATE = SIMSTATE_ONING;
			
			call PowerLED.clr();
			call PowerLED.makeInput();
			
			call UartControl.start();
			call UartStream.enableReceiveInterrupt();

			call PowerPin.set();
			call Timer.startOneShot(DELAY_POWER);
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStr("C-PW-ON");
			#endif
			
			call Leds.led3On();
			
			return SUCCESS;
		}
	}

	command error_t Sim900Control.stop()
	{
		
		ASSERT(SIMSTATE == SIMSTATE_IDLE || SIMSTATE == SIMSTATE_SOCKET_IDLE);
		
		if(SIMSTATE == SIMSTATE_IDLE || SIMSTATE == SIMSTATE_SOCKET_IDLE)
		{
			SIMSTATE = SIMSTATE_OFFING;
			call PowerPin.set();
			
			call Timer.startOneShot(DELAY_POWER);
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStr("C-PW-OFF");
			#endif
			
			return SUCCESS;
		}
		else return EINVAL;
	}
	
	command error_t Sim900DriverLayer.openSocket()
	{
		
		ASSERT(SIMSTATE == SIMSTATE_IDLE || SIMSTATE == SIMSTATE_SOCKET_IDLE);
	
		if(SIMSTATE == SIMSTATE_IDLE || SIMSTATE == SIMSTATE_SOCKET_IDLE)
		{
			SIMSTATE = SIMSTATE_SOCKET_CONN_DELAY;
			call Timer.startOneShot(DELAY_OPENSOCKET);
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStr("C-SOCK-CONN");
			#endif
			
			return SUCCESS;
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_CONN_DELAY || SIMSTATE == SIMSTATE_SOCKET_CONN) return EBUSY;
		else return EINVAL;
	}
	
	command error_t Sim900DriverLayer.writeSocket(char *data, uint8_t len)
	{
	
		ASSERT(SIMSTATE == SIMSTATE_SOCKET_IDLE);
	
		if(SIMSTATE == SIMSTATE_SOCKET_IDLE)
		{
			pDataSend = data;
			dataSendLen = len;
			
			SIMSTATE = SIMSTATE_SOCKET_SEND_CONS_DELAY;
			call Timer.startOneShot(DELAY_WR_SOCKET_CONS);
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStr("C-SOCK-WR");
			#endif
			
			return SUCCESS;
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_SEND_DELAY || SIMSTATE == SIMSTATE_SOCKET_SEND) return EBUSY;
		else return EINVAL;
	}
	
	command error_t Sim900DriverLayer.closeSocket()
	{
	
		/*
		 *TODO: This could be called for reset the socket. 
		 * If there is an usual no-answer situation, reset should be callable..
		 */
	
		ASSERT(SIMSTATE == SIMSTATE_IDLE || SIMSTATE == SIMSTATE_SOCKET_IDLE);
	
		if(SIMSTATE == SIMSTATE_IDLE || SIMSTATE == SIMSTATE_SOCKET_IDLE)
		{
			SIMSTATE = SIMSTATE_SOCKET_CLOSE_DELAY;
			call Timer.startOneShot(DELAY_CLOSE_SOCKET);
			
			#ifdef SIM900_DRIVER_DEBUG
			DiagStr("C-SOCK-CLS");
			#endif
			
			return SUCCESS;
		}
		else if(SIMSTATE == SIMSTATE_SOCKET_CONN_DELAY || SIMSTATE == SIMSTATE_SOCKET_CONN || SIMSTATE == SIMSTATE_SOCKET_SEND) return EBUSY;
		else return EINVAL;
	}

	command error_t Sim900DriverLayer.cmgs(char *phoneNum, char *data)
	{
		return FAIL;
	}

	async event void UartStream.sendDone(uint8_t *buf, uint16_t len, error_t error)
	{
		if(error != SUCCESS)
		{
			atSuccess = FAIL;
			post SignalDone();
		}
	}


	async event void UartStream.receiveDone(uint8_t *buf, uint16_t len, error_t error){
		// TODO Auto-generated method stub
	}

	

	event void Watchdog.fired()
	{
		atSuccess = FAIL;
		post SignalDone();
	}
	
	inline void uint8ToStr(uint8_t num, char *numStr)
	{
		uint8_t temp = 0;
		
		temp = (uint8_t)(num % 10);
		numStr[2] = numStrs[temp];
		num -= temp;
		
		temp = (uint8_t)(num % 100);
		numStr[1] = numStrs[temp / 10];
		num -= temp;
		
		temp = (uint8_t)(num % 1000);
		numStr[0] = numStrs[temp / 100];
		
		numStr[3] = '\0';	
	}

	command error_t Sim900DriverLayer.resetSocket()
	{
		return FAIL;
	}
}